#include <functional>
#include <gtest/gtest.h>
#include <variant>
#include "trt_define.h"

_TRT_INFER_BEGIN

class CPPTest : public ::testing::Test
{
protected:
    CPPTest() {}
    ~CPPTest() override {}
    void SetUp() override {}
    void TearDown() override {}
};

int func2(int a, int b) { return a + b; }

TEST_F(CPPTest, test_function)
{
    /**
     * std::function is a general-purpose polymorphic function wapper. It can hold, copy, and invoke any Callable target -- functions, lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to data members.
     *
     * How to use std::function:
     * 1. Declare a std::function object. std::function<return_type(Args... args)>
     * 2. Assign a callable object to the std::function object.
     * 3. Call the std::function object.
     */
    std::function<int(int, int)> func;
    func = [](int a, int b) -> int
    { return a + b; };
    EXPECT_EQ(func(1, 2), 3);

    func = func2;
    EXPECT_EQ(func(1, 2), 3);

    class Function
    {
    public:
        int operator()(int a, int b) { return a + b; }
    };

    func = Function();

    class CFunc
    {
    public:
        static int add(int a, int b) { return a + b; }
        int Func(int a, int b) { return a + b; }
    };

    func = CFunc::add;
    EXPECT_EQ(func(1, 2), 3);

    func = std::bind(&CFunc::Func, CFunc(), std::placeholders::_1, std::placeholders::_2);
    EXPECT_EQ(func(1, 2), 3);
    EXPECT_EQ(std::invoke(func, 1, 2), 3);
}

TEST_F(CPPTest, testOR)
{
    int dx = 11;
    int dy = 21;
    int fc = ((dx % 2) << 1) | (dy % 2);
    std::cout << "dx % 2: " << (dx % 2) << std::endl;             // 1
    std::cout << "dy % 2: " << (dy % 2) << std::endl;             // 0
    std::cout << "dx % 2 << 1: " << ((dx % 2) << 1) << std::endl; // 2
    std::cout << "fc: " << fc << std::endl;
}

TEST_F(CPPTest, testVist)
{
    /* The variant class template is a safe union type that can store a value of any of its alternative types (i.e., types specified in the variant declaration).
     */
    using value = std::variant<int, float, std::string>;
    std::vector<value> values = {10, 15.5f, "hello"};
    value v = "Hello, world";
    EXPECT_EQ(std::get<std::string>(v), "Hello, world");
    EXPECT_THROW(std::get<int>(v), std::bad_variant_access);
    //EXPECT_EQ(std::get_if<std::string>(v), "Hello, world");
    //EXPECT_EQ(std::get_if<int>(v), nullptr);
    EXPECT_EQ(v.index(), 2);

    std::visit([](auto &&arg) {
        using T = std::decay_t<decltype(arg)>;
        if constexpr (std::is_same_v<T, int>)
        {
            std::cout << "int: " << arg << std::endl;
        }
        else if constexpr (std::is_same_v<T, float>)
        {
            std::cout << "float: " << arg << std::endl;
        }
        else if constexpr (std::is_same_v<T, std::string>)
        {
            std::cout << "string: " << arg << std::endl;
        }
    }, v);

}

_TRT_INFER_END
